package com.fyyg.wechat.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.fyyg.common.exceptions.StandardException;
import com.fyyg.wechat.util.wechat.ErrorConstants;
import com.google.common.collect.Lists;

public class HttpClientUtils {

	static Logger logger = Logger.getLogger(HttpClientUtils.class);

	private static final int BUFF_SIZE = 8192;

	public static String doGet(String url, Map<String, String> params, String requestCharset, String responseCharset,
			int connectTimeout, int serviceTimeout) {

		HttpClient _htClnt = buildHttpClient(requestCharset, connectTimeout, serviceTimeout);

		GetMethod _gtMtd = new GetMethod(url);

		_gtMtd.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, requestCharset);

		buildGetParameters(_gtMtd, params);

		String _resp = executeMethod(url, responseCharset, _htClnt, _gtMtd);

		return _resp;

	}

	private static void buildGetParameters(GetMethod method, Map<String, String> params) {
		NameValuePair[] nameValuePairs = null;

		if (params != null && params.size() > 0) {
			nameValuePairs = new NameValuePair[params.size()];
			int idx = 0;
			for (String key : params.keySet()) {
				nameValuePairs[idx++] = new NameValuePair(key, params.get(key));
			}

		}

		if (nameValuePairs != null && nameValuePairs.length > 0) {
			method.setQueryString(nameValuePairs);
		}
	}

	private static HttpClient buildHttpClient(String requestCharset, int connectTimeout, int serviceTimeout) {
		SimpleHttpConnectionManager connectionManager = new SimpleHttpConnectionManager(true);
		connectionManager.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, requestCharset);
		connectionManager.getParams().setParameter(HttpMethodParams.HTTP_ELEMENT_CHARSET, requestCharset);
		connectionManager.getParams().setConnectionTimeout(connectTimeout);
		connectionManager.getParams().setSoTimeout(serviceTimeout);

		HttpClient _htClnt = new HttpClient(connectionManager);
		return _htClnt;
	}

	private static String executeMethod(String url, String responseCharset, HttpClient _htClnt, HttpMethod _gtMtd) {

		long start = System.currentTimeMillis();

		_gtMtd.releaseConnection();

		InputStream _respStm = null;
		String _resp = null;
		int status = 0;
		try {
			status = _htClnt.executeMethod(_gtMtd);

			_respStm = _gtMtd.getResponseBodyAsStream();

			byte[] _resBty = loadStream(_respStm);

			_resp = new String(_resBty, responseCharset);

			if (status != 200) {
				throw new StandardException(ErrorConstants.ERROR_NETWORK,
						format("远程状态码返回:?\r?", String.valueOf(status), _resp), null);
			}

		} catch (IOException e) {
			logger.error(url, e);
			throw new StandardException(ErrorConstants.ERROR_NETWORK, "远程连接异常", e);
		} finally {
			if (_respStm != null) {
				try {
					_respStm.close();
				} catch (IOException e) {
				}
			}

			shutdownClient(_htClnt, _gtMtd);

			long end = System.currentTimeMillis();
			long time = end - start;

			String message = word("[RemoteLog] ver=3 url={{0}} time={{1}} status={{2}}", url, String.valueOf(time),
					String.valueOf(status));

			if (time > 2000) {
				logger.warn(message);
			} else {
				logger.info(message);
			}
		}
		return _resp;
	}

	private static void shutdownClient(HttpClient httpClient, HttpMethod httpMethod) {
		if (httpMethod != null) {
			httpMethod.releaseConnection();
		}

		try {
			if (httpClient != null) {
				org.apache.commons.httpclient.HttpConnectionManager hcm = httpClient.getHttpConnectionManager();

				if (hcm != null) {
					if (hcm instanceof SimpleHttpConnectionManager) {
						SimpleHttpConnectionManager shcm = (SimpleHttpConnectionManager) hcm;
						shcm.shutdown();
					}

					hcm.closeIdleConnections(0);
				}
			}
		} catch (Throwable e) {
			// silent
		}
	}

	public static String word2(String wordPattern, Object... params) {

		List<String> list = Lists.newArrayList();
		if (ArrayUtils.isNotEmpty(params)) {
			for (Object object : params) {
				if (object == null) {
					list.add(StringUtils.EMPTY);
					continue;
				}
				list.add(object.toString());
			}
		}
		return word(wordPattern, list.toArray(new String[] {}));
	}

	public static String word(String wordPattern, String... params) {
		Pattern pattern = Pattern.compile("\\{\\d\\d*\\}");
		Matcher m = pattern.matcher(wordPattern);
		StringBuffer sb = new StringBuffer();

		while (m.find()) {
			String g = m.group();
			String idxStr = g.substring(1, g.length() - 1);
			int idx = Integer.valueOf(idxStr);
			String replacement = null;
			if (params == null || params.length <= idx) {
				replacement = StringUtils.EMPTY;
			} else {
				Object obj = params[idx];
				if (obj == null) {
					replacement = StringUtils.EMPTY;
				} else {
					if (obj instanceof BigDecimal) {
						replacement = formatBigDeciam((BigDecimal) obj, "###.##");
					} else {
						replacement = obj.toString();
					}
				}
			}

			String valueReplacement = replacement.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$", "\\\\\\$");
			m.appendReplacement(sb, valueReplacement);
		}

		m.appendTail(sb);

		return sb.toString();
	}

	public static String formatBigDeciam(BigDecimal d, String pattern) {
		if (d == null) {
			return StringUtils.EMPTY;
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(d);
	}

	public static byte[] loadStream(InputStream inputStream) {
		if (inputStream == null)
			return null;

		BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream, BUFF_SIZE);
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		byte[] buff = new byte[BUFF_SIZE];
		int length = 0;

		try {
			while ((length = bufferedInputStream.read(buff)) > 0) {
				byteArrayOutputStream.write(buff, 0, length);
			}
		} catch (IOException e) {
			throw new StandardException(ErrorConstants.ERROR_SERVICE_FATAL, "流读取异常", e);
		} finally {

			try {
				bufferedInputStream.close();
			} catch (IOException e) {
				throw new StandardException(ErrorConstants.ERROR_SERVICE_FATAL, "关闭流异常", e);
			}

		}

		byte[] bytes = byteArrayOutputStream.toByteArray();
		try {
			byteArrayOutputStream.flush();
		} catch (IOException e) {
			throw new StandardException(ErrorConstants.ERROR_SERVICE_FATAL, "刷新流异常", e);
		}
		try {
			byteArrayOutputStream.close();
		} catch (IOException e) {
			throw new StandardException(ErrorConstants.ERROR_SERVICE_FATAL, "关闭流异常", e);
		}
		return bytes;

	}

	public static String format(String str, String... param) {
		if (str == null) {
			return str;
		}

		StringBuffer sb = new StringBuffer(str);
		List<Integer> tagIndexList = Lists.newArrayList();

		int c = -1;

		while ((c = StringUtils.indexOf(str, '?', c)) >= 0) {
			tagIndexList.add(c++);
		}
		for (int i = tagIndexList.size(); i > 0; i--) {
			int idx = tagIndexList.get(i - 1);
			if (param == null || i > param.length) {
				sb.delete(idx, idx + 1);
			} else {
				String str2 = param[i - 1];
				sb.replace(idx, idx + 1, str2 == null ? "" : str2);
			}
		}
		return sb.toString();

	}
}
